package com.jacky.androidutils.database.dao.base;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.provider.ContactsContract.Data;

import com.jacky.androidutils.database.dao.annotation.Column;
import com.jacky.androidutils.database.dao.annotation.ID;
import com.jacky.androidutils.database.dao.annotation.TableName;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.List;

/*
 * 问题一： 表名的获取
 * 问题二： 如何将实体中的数据，按照对应关系导入数据库表中
 * 问题三： 将数据表中列的数据，按照对应关系导入实体中
 * 问题四： 明确实体中主键，获取主键中封装的值
 * 问题五： 实体对象的创建
 */
public abstract class DAOSupport<M> implements DAO<M> {
    private static final String TAG = "DAOSupport";
    private Context context;
    private DAOHelper helper;
    private SQLiteDatabase db;

    public DAOSupport(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
        super();
        this.context = context;
        helper = new DAOHelper(context, name, factory, version);
        db = helper.getWritableDatabase();
    }

    @Override
    public long insert(M m) {
        ContentValues values = new ContentValues();
//		values.put(DBHelper.TABLE_NEWS_TITLE, news.getTitle());
//		//此处省略n行代码
        fillcolumn(m, values);//第一个参数：数据源      第二个参数：导入目标
        return db.insert(getTableName(), null, values);
    }

    @Override
    public int delete(M m) {
        return db.delete(getTableName(), getIDName(m) + "=?", new String[]{getId(m)});
    }

    @Override
    public int update(M m) {
        ContentValues values = new ContentValues();
        fillcolumn(m, values);

        return db.update(getTableName(), values, getIDName(m) + "=?", new String[]{getId(m)});
    }

    @Override
    public List<M> findAll() {
        List<M> result = null;
        Cursor cursor = db.query(getTableName(), null, null, null, null, null, null);
        if (cursor != null) {
            result = new ArrayList<M>();
            while (cursor.moveToNext()) {
                M m = getInstance();

//				int columnIndex = cursor.getColumnIndex(DBHelper.TABLE_NEWS_TITLE);
//				String title = cursor.getString(columnIndex);
//				m.setTitle(title);
//				//此处省略n行代码

                fillField(cursor, m);
                result.add(m);
            }
            cursor.close();
        }
        return result;
    }


    /**
     * 问题一： 表名的获取
     *
     * @return
     */
    private String getTableName() {
        /*每个表对应一个具体实体
             方案一： 如果能够获取到实体，再获取到实体的简单名称，首字母小写
		 	方案二： 利用注解，实体和数据库表的名称脱离了关系
		 */
        //获取到实体
        //获取实体是注解，依据value里设置值确定操作的数据库表
        M m = getInstance();
        TableName tableName = m.getClass().getAnnotation(TableName.class);//annotationType注解类型
        if (tableName != null) {
            return tableName.value();
        }
        return "";
    }

    /**
     * 获取主键名
     *
     * @param m
     * @return
     */
    private String getIDName(M m) {
        Field[] fields = m.getClass().getDeclaredFields();
        for (Field item : fields) {
            item.setAccessible(true);
            ID id = item.getAnnotation(ID.class);
            if (id != null) {
                try {
                    return id.idname();
                } catch (Exception e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    /**
     * 问题二： 如何将实体中的数据，按照对应关系导入数据库表中
     */
    private void fillcolumn(M m, ContentValues values) {
//		values.put(DBHelper.TABLE_NEWS_TITLE, news.getTitle());
//		//此处省略n行代码
        Field[] fields = m.getClass().getDeclaredFields();
        for (Field item : fields) {
            item.setAccessible(true);
            Column column = item.getAnnotation(Column.class);//数据库表：列
            if (column != null) {
                String key = column.value();
                try {
                    String value = item.get(m).toString();//实体字段

                    //如果该field是主键，并且是自增；不能添加到集合中
                    ID id = item.getAnnotation(ID.class);
                    if (id != null && id.autoincrement()) {

                    } else {
                        values.put(key, value);
                    }
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

    /*
     * 问题三： 将数据表中列的数据，按照对应关系导入实体中
     */
    private void fillField(Cursor cursor, M m) {
//		int columnIndex = cursor.getColumnIndex(DBHelper.TABLE_NEWS_TITLE);
//		String title = cursor.getString(columnIndex);
//		m.setTitle(title);
//		//此处省略n行代码

        Field[] fields = m.getClass().getDeclaredFields();
        for (Field item : fields) {
            item.setAccessible(true);
            Column column = item.getAnnotation(Column.class);
            if (column != null) {
                int columnIndex = cursor.getColumnIndex(column.value());
                String value = cursor.getString(columnIndex);
                //Sets the value of the field in the specified object to the value.
                try {
                    if (item.getType() == int.class) {
                        item.set(m, Integer.parseInt(value));
                    } else if (item.getType() == Data.class) {
                        //字符串转成时间
                    } else {
                        item.set(m, value);
                    }
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 问题四： 明确实体中主键，获取主键中封装的值
     */
    private String getId(M m) {
        Field[] fields = m.getClass().getDeclaredFields();
        for (Field item : fields) {
            item.setAccessible(true);
            ID id = item.getAnnotation(ID.class);
            if (id != null) {
                try {
                    return item.get(m).toString();
                } catch (IllegalArgumentException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
            }
        }
        return null;
    }

    /**
     * 问题五： 实体对象的创建
     *
     * @return
     */
    private M getInstance() {
        //实体何时确定
        /*
		 * ①哪个孩子调用的方法==哪个孩子在运行
		 * ②获取该孩子的父类（“支持泛型”的父类）
		 * ③获取到泛型中的参数
		 */

        //①哪个孩子调用的方法==哪个孩子在运行
        Class clazz = getClass();
//		Log.i(TAG, clazz.toString());

        //②获取该孩子的父类（“支持泛型”的父类）
//		clazz.getSuperclass();//class com.cl.db65.dao.base.DAOSupport
        Type superclass = clazz.getGenericSuperclass();//com.cl.db65.dao.base.DAOSupport<com.cl.db65.dao.domain.News>

        //③获取到泛型中的参数
        //泛型实现接口（参数化的类型ParameterizedType），规定了泛型的通用操作
        if (superclass != null && superclass instanceof ParameterizedType) {
            Type[] arguments = ((ParameterizedType) superclass).getActualTypeArguments();//[class com.cl.db65.dao.domain.News]
            try {
                return (M) ((Class) arguments[0]).newInstance();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
            //Log.i(TAG, "");
        }
        return null;
    }
}
